package com.noahsloan.nutils;

/**
 * Simple wrapper around a value. Used for pass by reference parameters. You may
 * prefer to use the <a
 * href="http://jakarta.apache.org/commons/lang/api-release/org/apache/commons/lang/mutable/package-summary.html">Apache
 * Commons Mutable</a> for primitive types, although for Objects, this is type
 * safe while their MutableObject is not.
 * 
 * @author Noah Sloan
 * 
 * @param <T>
 *            the type to wrap.
 */
public final class Holder<T> implements Comparable<Holder<T>> {

    private T value; // the wrapped value

    /**
     * Start off wrapping null.
     */
    public Holder() {
        this(null); // not strictly necessary, but what they hey
    }

    /**
     * 
     * @param value
     *            the value to wrap
     */
    public Holder(T value) {
        this.value = value;
    }

    /**
     * 
     * @return the wrapped value.
     */
    public T getValue() {
        return value;
    }

    /**
     * 
     * @param value
     *            the change to the wrapped value
     */
    public void setValue(T value) {
        this.value = value;
    }

    @Override
    /**
     * Returns true if and only if the other value is a holder and
     * <code>this.value.equals(o.value)</code> or both values are null.
     */
    public boolean equals(Object o) {
        if (o instanceof Holder) {
            Holder<?> h = (Holder<?>) o;
            if (this.value == null) {
                return h.value == null;
            }
            return this.value.equals(h.value);
        }
        return false;
    }

    @Override
    /**
     * Returns the hashCode of the wrapped value or 0 if the value is null.
     */
    public int hashCode() {
        return value != null ? value.hashCode() : 0;
    }

    @Override
    /**
     * Returns the toString of the wrapped value, or null if nothing is wrapped.
     */
    public String toString() {
        return value != null ? value.toString() : null;
    }

    /**
     * Compares the value wrapped by this Holder to the passed in value. If both
     * values are null, this returns 0.
     * 
     * @param h
     *            the other Holder
     * @return <code>this.value.compareTo(h.value)</code>
     * @throws ClassCastException
     *             if T is not Comparable
     * @throws NullPointerException
     *             if the wrapped value is <code>null</code> and
     *             <code>h.value.compareTo(null)</code> throws a
     *             {@link NullPointerException} or <code>h.value</code> is
     *             <code>null</code> and
     *             <code>this.value.compareTo(null)</code> throws a
     *             {@link NullPointerException}.
     */
    @SuppressWarnings("unchecked")
    public int compareTo(Holder<T> h) {
        if (value == null) {
            if (h.value == null) {
                return 0;
            }
            if (h.value instanceof Comparable) {
                Comparable v = (Comparable) h.value;
                // by the general contract of compareTo, this is either
                // equivalent or will thrown an exception
                return -v.compareTo(null);
            }
        }
        if (value instanceof Comparable) {
            Comparable v = (Comparable) value;
            return v.compareTo(h.value);
        }
        throw new ClassCastException(value == null ? null : value.getClass()
                + " is not Comparable!");
    }
}
